//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: SqlRawStorageAccess.cs
//
//  Description: This class allows raw access to the SQL storages.
// 
//--------------------------------------------------------------------------


using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Chili.Opf3.Query;

namespace Chili.Opf3.Storages
{
	/// <summary>
	/// Allows access to the raw ADO.NET storage objects.
	/// </summary>
	/// <remarks>
	/// This class gives access to the raw ADO.NET storage objects, created by storages that inherit from
	/// <see cref="SqlStorageBase">SqlStorageBase</see> (all storages that access an SQL database).
	/// </remarks>
	/// <example>
	///     The following example shows how to use this class to get access to the raw objects.
	///     <code lang="CS">
	/// // ... Other code
	///  
	/// // Creates a new ObjectContext that uses an Oracle database as storage.
	/// // You should only pass an instance of the storage to your ObjectContext.
	/// // That's it! Nothing more. The methods of the storage are accessed
	/// // by the context internally.
	/// ObjectContext context = new ObjectContext(
	///     new OracleStorage("... some ConnectionString ..."));
	/// 
	/// SqlRawStorageAccess raw = 
	///		new SqlRawStorageAccess(context.Storage as SqlStorageBase);
	/// // Use the raw connection from the storage.
	/// IDbConnection connection = raw.GetConnection();
	/// // Get the raw command from the storage.
	/// IDbCommand command = raw.CreateCommand("select * from Foo");
	/// 
	/// // ... Other code
	///     </code>
	/// </example>
	public class SqlRawStorageAccess
	{
		private SqlStorageBase _storage;
		private int _index;

		/// <summary>
		/// Creates a new instance of the <see cref="SqlRawStorageAccess">SqlRawStorageAccess Class</see>.
		/// </summary>
		/// <param name="storage">The storage that's raw objects are returned.</param>
		public SqlRawStorageAccess(SqlStorageBase storage)
		{
			if (storage == null)
				throw new ArgumentNullException("storage");
			_storage = storage;
		}

		/// <summary>
		/// Gets the connection of the storage. If a transaction is active
		/// the connection of the transaction is returned. If no transaction is active,
		/// a new connection is created and returned.
		/// </summary>
		/// <returns>A connection to the physical storage (database).</returns>
		public IDbConnection GetConnection()
		{
			SqlTransactionBase transaction = (SqlTransactionBase)_storage.ActiveTransaction;
			if (transaction != null)
				return transaction.InnerTransaction.Connection;
				
			return _storage.CreateConnection();
		}

		/// <summary>
		/// Gets the current transaction. If no transaction is active null is returned.
		/// </summary>
		/// <returns>The active transaction or null if none is active.</returns>
		public IDbTransaction GetCurrentTransaction()
		{
			SqlTransactionBase transaction = (SqlTransactionBase)_storage.ActiveTransaction;
			if (transaction != null)
				return transaction.InnerTransaction;
			return null;
		}

		/// <summary>
		/// Creates a command for the storage. The specified connection is set
		/// as connection of the command.
		/// </summary>
		/// <param name="connection">The connection that is associated with the command.</param>
		/// <returns>A command object that may be executed on the storage.</returns>
		public IDbCommand CreateCommand(IDbConnection connection)
		{
			if (connection == null)
				throw new ArgumentNullException("connection");

			IDbCommand command = _storage.CreateCommand();
			// set the specified connection.
			command.Connection = connection;

			return command;
		}

		/// <summary>
		/// Creates a command for the storage. The current transasction and connection
		/// is set automatically.
		/// </summary>
		/// <returns>A command object that may be executed on the storage.</returns>
		public IDbCommand CreateCommand()
		{
			IDbCommand command = _storage.CreateCommand();
			return SetCommandProperties(command);
		}

		/// <summary>
		/// Sets the command properties.
		/// </summary>
		/// <param name="command">The command whose properties are set.</param>
		/// <returns></returns>
		private IDbCommand SetCommandProperties(IDbCommand command)
		{
			if (command == null)
				throw new ArgumentNullException("command");

			// Set the connection.
			command.Connection = this.GetConnection();

			// Set the current transaction.
			IDbTransaction transaction = this.GetCurrentTransaction();
			if (transaction != null)
				command.Transaction = transaction;

			return command;
		}

		/// <summary>
		/// Creates a command for the storage. The current transasction and connection
		/// is set automatically.
		/// </summary>
		/// <param name="text">The text for the command.</param>
		/// <returns>A command object that may be executed on the storage.</returns>
		public IDbCommand CreateCommand(string text)
		{
			if (text == null)
				throw new ArgumentNullException("text");

			IDbCommand command = this.CreateCommand();

			// Add the text to the command.
			command.CommandText = text;

			return command;
		}

		/// <summary>
		/// Creates a command for the storage. The current transasction and connection
		/// is set automatically.
		/// </summary>
		/// <param name="text">The text for the command.</param>
		/// <param name="parameters">The parameter for the command.</param>
		/// <returns>A command object that may be executed on the storage.</returns>
		public IDbCommand CreateCommand(string text, params IDataParameter[] parameters)
		{
			if (text == null)
				throw new ArgumentNullException("text");
			if (parameters == null)
				throw new ArgumentNullException("parameters");

			IDbCommand command = this.CreateCommand(text);

			// Add the parameters to the command.
			foreach(IDataParameter parameter in parameters)
			{
				command.Parameters.Add(parameters);
			}

			return command;
		}

		/// <summary>
		/// Creates a command for the storage. The current transaction and connection
		/// is set automatically.
		/// </summary>
		/// <param name="query">The query that is transformed to a command.</param>
		/// <returns>A command object that may be executed on the storage.</returns>
		public IDbCommand CreateCommand(IQuery query)
		{
			if (query == null)
				throw new ArgumentNullException("query");

			CompiledQuery q = query.ToCompiledQuery(_storage);
			IDbCommand command = q.Command;

			// Set the properties of the command.
			return SetCommandProperties(command);
		}

		/// <summary>
		/// Creates a parameter for the storage.
		/// </summary>
		/// <param name="name">The name of the parameter. Is passed as reference, because may be changed by the storage!</param>
		/// <param name="value">The value of the parameter.</param>
		/// <param name="type">The type of the value of the parameter. This is especially required if the parameter value is null.</param>
		/// <returns>An instance of a parameter that may be used with a command.</returns>
		public IDataParameter CreateParameter(ref string name, object value, Type type)
		{
			IDataParameter parameter = _storage.CreateParameter(_index, ref name, value, type);

			// Increment the current index.
			_index++;

			return parameter;
		}
	}
}
